Ejemplo n.º 1
0
void BExtractorDoc::FloodFillDIB(CDib *bm, CProgressDlg *prog)
{
	int width = bm->GetWidth();
	int height = bm->GetHeight();
	byte target = 0x00;
	int result = 0;

	for (int i = 0; i < width; i++)
	{
		if ((i % 10) == 0)
			prog->SetPos(i * 200 / width);
		for (int j = 0; j < height; j++)
		{
			if (bm->GetPixel8(i,j) == target)
			{
				result = Fill(bm, i, j, 0x00, 0xff, width, height, 1);
				DPoint2 point;
				point.x = i_UTMx(i);
				point.y = i_UTMy(j);

				// rough heuristic: adjusts the coordinate more towards the
				//center of the original building
				point.x += 5;

				vtBuilding *bld = new vtBuilding;
				bld->SetRectangle(point, 10, 10);
				bld->SetStories(1);
				bld->GetLevel(1)->SetEdgeColor(RGBi(255,255,255));	// white roof
				m_Buildings.Append(bld);
			}
		}
	}
}
Ejemplo n.º 2
0
bool SetupDefaultStructures(const vtString &fname)
{
	if (fname != "")
	{
		if (g_DefaultStructures.ReadXML(fname))
			return true;
	}

	// else supply some internal defaults and let the user know the load failed
	vtBuilding *pBld = g_DefaultStructures.NewBuilding();
	vtLevel *pLevel;
	DPolygon2 DefaultFootprint; // Single edge
	DefaultFootprint.resize(1);
	DefaultFootprint[0].Append(DPoint2(0.0, 0.0));
	DefaultFootprint[0].Append(DPoint2(0.0, 1.0));

	// The default building is NOT a complete building
	// Alter the code here to set different hard coded
	// defaults for use in other operations
	// First set any structure tags needed
	//
	// NONE
	//
	// Now create the required number of levels
	// and set the values
	//
	// Level 0
	pLevel = pBld->CreateLevel(DefaultFootprint);
	pLevel->m_iStories = 1;
	pLevel->m_fStoryHeight = 3.20f;
	pLevel->SetEdgeMaterial(BMAT_NAME_PLAIN);
	pLevel->SetEdgeColor(RGBi(255,0,0)); // Red
	pLevel->GetEdge(0)->m_iSlope = 90;
	// Level 1
	pLevel = pBld->CreateLevel(DefaultFootprint);
	pLevel->m_iStories = 1;
	pLevel->m_fStoryHeight = 3.20f;
	pLevel->SetEdgeMaterial(BMAT_NAME_PLAIN);
	pLevel->SetEdgeColor(RGBi(255,240,225)); // Tan
	pLevel->GetEdge(0)->m_iSlope = 0;		 // Flat

	g_DefaultStructures.Append(pBld);

	return false;
}
Ejemplo n.º 3
0
void Style::LoadFactoryDefaults() {
    const Default *d;
    for(d = &(Defaults[0]); d->h.v; d++) {
        Style *s = Get(d->h);
        FillDefaultStyle(s, d, /*factory=*/true);
    }
    SS.backgroundColor = RGBi(0, 0, 0);
    SS.bgImage.pixmap.Clear();
}
Ejemplo n.º 4
0
int vtStructureArray::AddFoundations(vtHeightField *pHF, bool progress_callback(int))
{
	vtLevel *pLev, *pNewLev;
	int i, j, pts, built = 0;
	float fElev;

	int selected = NumSelected();
	int size = GetSize();
	VTLOG("AddFoundations: %d selected, %d total, ", selected, size);

	for (i = 0; i < size; i++)
	{
		if (progress_callback != NULL)
			progress_callback(i * 99 / size);

		vtStructure *str = GetAt(i);
		vtBuilding *bld = str->GetBuilding();
		if (!bld)
			continue;
		if (selected > 0 && !str->IsSelected())
			continue;

		// Get the outer footprint of the lowest level
		pLev = bld->GetLevel(0);
		const DLine2 &foot = pLev->GetOuterFootprint();
		pts = foot.GetSize();

		float fMin = 1E9, fMax = -1E9;
		for (j = 0; j < pts; j++)
		{
			pHF->FindAltitudeOnEarth(foot.GetAt(j), fElev);

			if (fElev < fMin) fMin = fElev;
			if (fElev > fMax) fMax = fElev;
		}
		float fDiff = fMax - fMin;

		// if there's less than 50cm of depth, don't bother building
		// a foundation
		if (fDiff < 0.5f)
			continue;

		// Create and add a foundation level
		pNewLev = new vtLevel;
		pNewLev->m_iStories = 1;
		pNewLev->m_fStoryHeight = fDiff;
		bld->InsertLevel(0, pNewLev);
		bld->SetFootprint(0, foot);
		pNewLev->SetEdgeMaterial(BMAT_NAME_CEMENT);
		pNewLev->SetEdgeColor(RGBi(255, 255, 255));
		built++;
	}
	VTLOG("%d added.\n", built);
	return built;
}
Ejemplo n.º 5
0
//-----------------------------------------------------------------------------
// If a color is almost white, then we can rewrite it to black, just so that
// it won't disappear on file formats with a light background.
//-----------------------------------------------------------------------------
RgbColor Style::RewriteColor(RgbColor rgbin) {
    Vector rgb = Vector::From(rgbin.redF(), rgbin.greenF(), rgbin.blueF());
    rgb = rgb.Minus(Vector::From(1, 1, 1));
    if(rgb.Magnitude() < 0.4 && SS.fixExportColors) {
        // This is an almost-white color in a default style, which is
        // good for the default on-screen view (black bg) but probably
        // not desired in the exported files, which typically are shown
        // against white backgrounds.
        return RGBi(0, 0, 0);
    } else {
        return rgbin;
    }
}
Ejemplo n.º 6
0
vtAbstractLayer *CreateNewAbstractPointLayer(vtTerrain *pTerr, bool bAskStyle)
{
	// make a new abstract layer (points)
	vtFeatureSetPoint2D *pSet = new vtFeatureSetPoint2D;
	pSet->SetFilename("Untitled.shp");
	pSet->AddField("Label", FT_String);

	// Inherit projection
	pSet->SetProjection(pTerr->GetProjection());

	// Ask style for the new point layer
	vtTagArray props;
	props.SetValueBool("ObjectGeometry", false, true);
	props.SetValueBool("Labels", true, true);
	props.SetValueRGBi("LabelColor", RGBi(255,255,0), true);
	props.SetValueFloat("LabelHeight", 10.0f, true);
	props.SetValueInt("TextFieldIndex", 0, true);
	props.SetValueBool("LabelOutline", true, true);

	if (bAskStyle)
	{
		StyleDlg dlg(NULL, -1, _("Style"));
		dlg.SetFeatureSet(pSet);
		dlg.SetOptions(props);
		if (dlg.ShowModal() != wxID_OK)
		{
			delete pSet;
			return NULL;
		}
		dlg.GetOptions(props);
	}

	// wrap the features in an abstract layer
	vtAbstractLayer *ab_layer = new vtAbstractLayer;
	ab_layer->SetFeatureSet(pSet);
	ab_layer->AddProps(props);

	// add the new layer to the terrain
	pTerr->GetLayers().push_back(ab_layer);
	pTerr->SetActiveLayer(ab_layer);

	// Construct it once so it is set up for future visuals.
	pTerr->CreateAbstractLayerVisuals(ab_layer);

	// and show it in the layers dialog
	GetFrame()->m_pLayerDlg->RefreshTreeContents();	// full refresh

	return ab_layer;
}
Ejemplo n.º 7
0
vtAbstractLayer *CreateNewAbstractLineLayer(vtTerrain *pTerr, bool bAskStyle)
{
	// make a new abstract layer (points)
	vtFeatureSetLineString *pSet = new vtFeatureSetLineString;
	pSet->SetFilename("Untitled.shp");
	pSet->AddField("Elevation", FT_Float);

	// Ask style for the new point layer
	vtTagArray props;
	props.SetValueBool("LineGeometry", true, true);
	props.SetValueRGBi("LineGeomColor", RGBi(255,255,0), true);
	props.SetValueFloat("LineGeomHeight", 10.0f, true);
	props.SetValueBool("Labels", false, true);

	if (bAskStyle)
	{
		StyleDlg dlg(NULL, -1, _("Style"));
		dlg.SetFeatureSet(pSet);
		dlg.SetOptions(props);
		// Show the "Line Geometry" page, which is most relevant for a line layer
		dlg.GetNotebook()->SetSelection(1);
		if (dlg.ShowModal() != wxID_OK)
		{
			delete pSet;
			return NULL;
		}
		dlg.GetOptions(props);
	}

	// wrap the features in an abstract layer
	vtAbstractLayer *ab_layer = new vtAbstractLayer();
	ab_layer->SetFeatureSet(pSet);
	ab_layer->SetProps(props);

	// add the new layer to the terrain
	pTerr->GetLayers().push_back(ab_layer);
	pTerr->SetActiveLayer(ab_layer);

	// Construct it once so it is set up for future visuals.
	pTerr->CreateAbstractLayerVisuals(ab_layer);

	// and show it in the layers dialog
	GetFrame()->m_pLayerDlg->RefreshTreeContents();	// full refresh

	return ab_layer;
}
Ejemplo n.º 8
0
void Style::LoadFactoryDefaults(void) {
    const Default *d;
    for(d = &(Defaults[0]); d->h.v; d++) {
        Style *s = Get(d->h);

        s->color        = d->color;
        s->width        = d->width;
        s->widthAs      = UNITS_AS_PIXELS;
        s->textHeight   = DEFAULT_TEXT_HEIGHT;
        s->textHeightAs = UNITS_AS_PIXELS;
        s->textOrigin   = 0;
        s->textAngle    = 0;
        s->visible      = true;
        s->exportable   = true;
        s->filled       = false;
        s->fillColor    = RGBf(0.3, 0.3, 0.3);
        s->name.strcpy(CnfPrefixToName(d->cnfPrefix));
    }
    SS.backgroundColor = RGBi(0, 0, 0);
    if(SS.bgImage.fromFile) MemFree(SS.bgImage.fromFile);
    SS.bgImage.fromFile = NULL;
}
Ejemplo n.º 9
0
/**
 * Get the color of the building.  In the case of multi-colored buildings,
 *  note that this method returns only the first color encountered.
 *
 * \param which Can be either BLD_BASIC (color of the building)	or BLD_ROOF
 *		(color of the roof).
 */
RGBi vtBuilding::GetColor(BldColor which) const
{
	int i, levs = m_Levels.GetSize();
	for (i = 0; i < levs; i++)
	{
		vtLevel *pLev = m_Levels[i];
		int j, edges = pLev->NumEdges();
		for (j = 0; j < edges; j++)
		{
			vtEdge *edge = pLev->GetEdge(j);
			if (edge->m_iSlope < 90)
			{
				if (which == BLD_ROOF)
					return edge->m_Color;
			}
			else
			{
				if (which == BLD_BASIC)
					return edge->m_Color;
			}
		}
	}
	return RGBi(0,0,0);
}
Ejemplo n.º 10
0
void SolveSpaceUI::Init() {
    SS.tangentArcRadius = 10.0;

    // Then, load the registry settings.
    int i;
    // Default list of colors for the model material
    modelColor[0] = CnfThawColor(RGBi(150, 150, 150), "ModelColor_0");
    modelColor[1] = CnfThawColor(RGBi(100, 100, 100), "ModelColor_1");
    modelColor[2] = CnfThawColor(RGBi( 30,  30,  30), "ModelColor_2");
    modelColor[3] = CnfThawColor(RGBi(150,   0,   0), "ModelColor_3");
    modelColor[4] = CnfThawColor(RGBi(  0, 100,   0), "ModelColor_4");
    modelColor[5] = CnfThawColor(RGBi(  0,  80,  80), "ModelColor_5");
    modelColor[6] = CnfThawColor(RGBi(  0,   0, 130), "ModelColor_6");
    modelColor[7] = CnfThawColor(RGBi( 80,   0,  80), "ModelColor_7");
    // Light intensities
    lightIntensity[0] = CnfThawFloat(1.0f, "LightIntensity_0");
    lightIntensity[1] = CnfThawFloat(0.5f, "LightIntensity_1");
    ambientIntensity = 0.3; // no setting for that yet
    // Light positions
    lightDir[0].x = CnfThawFloat(-1.0f, "LightDir_0_Right"     );
    lightDir[0].y = CnfThawFloat( 1.0f, "LightDir_0_Up"        );
    lightDir[0].z = CnfThawFloat( 0.0f, "LightDir_0_Forward"   );
    lightDir[1].x = CnfThawFloat( 1.0f, "LightDir_1_Right"     );
    lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up"        );
    lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward"   );

    exportMode = false;
    // Chord tolerance
    chordTol = CnfThawFloat(0.5f, "ChordTolerancePct");
    // Max pwl segments to generate
    maxSegments = CnfThawInt(10, "MaxSegments");
    // Chord tolerance
    exportChordTol = CnfThawFloat(0.1f, "ExportChordTolerance");
    // Max pwl segments to generate
    exportMaxSegments = CnfThawInt(64, "ExportMaxSegments");
    // View units
    viewUnits = (Unit)CnfThawInt((uint32_t)UNIT_MM, "ViewUnits");
    // Number of digits after the decimal point
    afterDecimalMm = CnfThawInt(2, "AfterDecimalMm");
    afterDecimalInch = CnfThawInt(3, "AfterDecimalInch");
    // Camera tangent (determines perspective)
    cameraTangent = CnfThawFloat(0.3f/1e3f, "CameraTangent");
    // Grid spacing
    gridSpacing = CnfThawFloat(5.0f, "GridSpacing");
    // Export scale factor
    exportScale = CnfThawFloat(1.0f, "ExportScale");
    // Export offset (cutter radius comp)
    exportOffset = CnfThawFloat(0.0f, "ExportOffset");
    // Rewrite exported colors close to white into black (assuming white bg)
    fixExportColors = CnfThawBool(true, "FixExportColors");
    // Draw back faces of triangles (when mesh is leaky/self-intersecting)
    drawBackFaces = CnfThawBool(true, "DrawBackFaces");
    // Check that contours are closed and not self-intersecting
    checkClosedContour = CnfThawBool(true, "CheckClosedContour");
    // Export shaded triangles in a 2d view
    exportShadedTriangles = CnfThawBool(true, "ExportShadedTriangles");
    // Export pwl curves (instead of exact) always
    exportPwlCurves = CnfThawBool(false, "ExportPwlCurves");
    // Background color on-screen
    backgroundColor = CnfThawColor(RGBi(0, 0, 0), "BackgroundColor");
    // Whether export canvas size is fixed or derived from bbox
    exportCanvasSizeAuto = CnfThawBool(true, "ExportCanvasSizeAuto");
    // Margins for automatic canvas size
    exportMargin.left   = CnfThawFloat(5.0f, "ExportMargin_Left");
    exportMargin.right  = CnfThawFloat(5.0f, "ExportMargin_Right");
    exportMargin.bottom = CnfThawFloat(5.0f, "ExportMargin_Bottom");
    exportMargin.top    = CnfThawFloat(5.0f, "ExportMargin_Top");
    // Dimensions for fixed canvas size
    exportCanvas.width  = CnfThawFloat(100.0f, "ExportCanvas_Width");
    exportCanvas.height = CnfThawFloat(100.0f, "ExportCanvas_Height");
    exportCanvas.dx     = CnfThawFloat(  5.0f, "ExportCanvas_Dx");
    exportCanvas.dy     = CnfThawFloat(  5.0f, "ExportCanvas_Dy");
    // Extra parameters when exporting G code
    gCode.depth         = CnfThawFloat(10.0f, "GCode_Depth");
    gCode.passes        = CnfThawInt(1, "GCode_Passes");
    gCode.feed          = CnfThawFloat(10.0f, "GCode_Feed");
    gCode.plungeFeed    = CnfThawFloat(10.0f, "GCode_PlungeFeed");
    // Show toolbar in the graphics window
    showToolbar = CnfThawBool(true, "ShowToolbar");
    // Recent files menus
    for(i = 0; i < MAX_RECENT; i++) {
        RecentFile[i] = CnfThawString("", "RecentFile_" + std::to_string(i));
    }
    RefreshRecentMenus();
    // Autosave timer
    autosaveInterval = CnfThawInt(5, "AutosaveInterval");

    // The default styles (colors, line widths, etc.) are also stored in the
    // configuration file, but we will automatically load those as we need
    // them.

    SetAutosaveTimerFor(autosaveInterval);

    NewFile();
    AfterNewFile();
}
Ejemplo n.º 11
0
void vtElevLayer::RenderBitmap()
{
	if (!m_pGrid)
		return;

	// flag as being rendered
	m_bNeedsDraw = false;

	// safety check
	if (m_ImageSize.x == 0 || m_ImageSize.y == 0)
		return;

	DetermineMeterSpacing();

	clock_t tm1 = clock();

#if 0
	// TODO: re-enable this friendly cancel behavior
	if (UpdateProgressDialog(j*100/m_ImageSize.y))
	{
		wxString msg = _("Turn off displayed elevation for elevation layers?");
		if (wxMessageBox(msg, _T(""), wxYES_NO) == wxYES)
		{
			m_draw.m_bShowElevation = false;
			CloseProgressDialog();
			return;
		}
		else
			ResumeProgressDialog();
	}
#endif
	ColorMap cmap;
	vtString cmap_fname = m_draw.m_strColorMapFile;
	vtString cmap_path = FindFileOnPaths(vtGetDataPath(), "GeoTypical/" + cmap_fname);
	bool bLoaded = false;
	if (cmap_path != "")
	{
		if (cmap.Load(cmap_path))
			bLoaded = true;
	}
	if (!bLoaded)
		SetupDefaultColors(cmap);

	bool has_invalid = m_pGrid->ColorDibFromElevation(m_pBitmap, &cmap,
		8000, RGBi(255,0,0), progress_callback_minor);

	if (m_draw.m_bShadingQuick)
		m_pGrid->ShadeQuick(m_pBitmap, SHADING_BIAS, true, progress_callback_minor);
	else if (m_draw.m_bShadingDot)
	{
		// Quick and simple sunlight vector
		FPoint3 light_dir = LightDirection(m_draw.m_iCastAngle, m_draw.m_iCastDirection);

		if (m_draw.m_bCastShadows)
			m_pGrid->ShadowCastDib(m_pBitmap, light_dir, 1.0f,
				m_draw.m_fAmbient, progress_callback_minor);
		else
			m_pGrid->ShadeDibFromElevation(m_pBitmap, light_dir, 1.0f,
				m_draw.m_fAmbient, m_draw.m_fGamma, true, progress_callback_minor);
	}

	m_pBitmap->ContentsChanged();

	if (has_invalid && m_draw.m_bDoMask)
	{
		m_pMask = new wxMask(*m_pBitmap->m_pBitmap, wxColour(255, 0, 0));
		m_pBitmap->m_pBitmap->SetMask(m_pMask);
		m_bHasMask = true;
	}
	else
		m_bHasMask = false;

	clock_t tm2 = clock();
	float time = ((float)tm2 - tm1)/CLOCKS_PER_SEC;
	VTLOG("RenderBitmap: %.3f seconds.\n", time);

	m_bBitmapRendered = true;
}
Ejemplo n.º 12
0
void vtElevLayer::SetupDefaultColors(ColorMap &cmap)
{
	cmap.m_bRelative = false;
	cmap.Add(-400*17, RGBi(255, 255, 255));
	cmap.Add(-400*16, RGBi(20, 20, 30	));
	cmap.Add(-400*15, RGBi(60, 60, 70	));
	cmap.Add(-400*14, RGBi(120, 120, 130));
	cmap.Add(-400*13, RGBi(180, 185, 190));
	cmap.Add(-400*12, RGBi(160, 80, 0	));
	cmap.Add(-400*11, RGBi(128, 128, 0	));
	cmap.Add(-400*10, RGBi(160, 0, 160	));
	cmap.Add(-400* 9, RGBi(144, 64, 144	));
	cmap.Add(-400* 8, RGBi(128, 128, 128));
	cmap.Add(-400* 7, RGBi(64, 128, 60	));
	cmap.Add(-400* 6, RGBi(0, 128, 0	));
	cmap.Add(-400* 5, RGBi(0, 128, 128	));
	cmap.Add(-400* 4, RGBi(0, 0, 160	));
	cmap.Add(-400* 3, RGBi(43, 90, 142	));
	cmap.Add(-400* 2, RGBi(81, 121, 172	));
	cmap.Add(-400* 1, RGBi(108, 156, 195));
	cmap.Add(-1.0f,  RGBi(182, 228, 255));
	cmap.Add(0, RGBi(0, 0, 0xee));
	cmap.Add( 0.1f,  RGBi(40, 224, 40	));
	cmap.Add( 450* 1, RGBi(0, 128, 0	));
	cmap.Add( 450* 2, RGBi(100, 144, 76	));
	cmap.Add( 450* 3, RGBi(204, 170, 136));
	cmap.Add( 450* 4, RGBi(136, 100, 70	));
	cmap.Add( 450* 5, RGBi(128, 128, 128));
	cmap.Add( 450* 6, RGBi(180, 128, 64	));
	cmap.Add( 450* 7, RGBi(255, 144, 32	));
	cmap.Add( 450* 8, RGBi(200, 110, 80	));
	cmap.Add( 450* 9, RGBi(160, 80, 160	));
	cmap.Add( 450*10, RGBi(144, 40, 128	));
	cmap.Add( 450*11, RGBi(128, 128, 128));
	cmap.Add( 450*12, RGBi(255, 255, 255));
	cmap.Add( 450*13, RGBi(255, 255, 128));
	cmap.Add( 450*14, RGBi(255, 128, 0));
	cmap.Add( 450*15, RGBi(0, 128, 0));
}
Ejemplo n.º 13
0
bool vtElevLayer::WriteElevationTileset(TilingOptions &opts, BuilderView *pView)
{
	// Avoid trouble with '.' and ',' in Europe
	ScopedLocale normal_numbers(LC_NUMERIC, "C");

	// Check that options are valid
	CheckCompressionMethod(opts);

	// grid size
	int base_tilesize = opts.lod0size;

	int gridcols, gridrows;
	m_pGrid->GetDimensions(gridcols, gridrows);

	DRECT area = m_pGrid->GetEarthExtents();
	DPoint2 tile_dim(area.Width()/opts.cols, area.Height()/opts.rows);
	DPoint2 cell_size = tile_dim / base_tilesize;

	const vtProjection &proj = m_pGrid->GetProjection();
	vtString units = GetLinearUnitName(proj.GetUnits());
	units.MakeLower();
	int zone = proj.GetUTMZone();
	vtString crs;
	if (proj.IsGeographic())
		crs = "LL";
	else if (zone != 0)
		crs = "UTM";
	else
		crs = "Other";

	// Try to create directory to hold the tiles
	vtString dirname = opts.fname;
	RemoveFileExtensions(dirname);
	if (!vtCreateDir(dirname))
		return false;

	// We won't know the exact height extents until the tiles have generated,
	//  so gather extents as we produce the tiles and write the INI later.
	float minheight = 1E9, maxheight = -1E9;

	ColorMap cmap;
	vtElevLayer::SetupDefaultColors(cmap);	// defaults
	vtString dirname_image = opts.fname_images;
	RemoveFileExtensions(dirname_image);
	if (opts.bCreateDerivedImages)
	{
		if (!vtCreateDir(dirname_image))
			return false;

		vtString cmap_fname = opts.draw.m_strColorMapFile;
		vtString cmap_path = FindFileOnPaths(vtGetDataPath(), "GeoTypical/" + cmap_fname);
		if (cmap_path == "")
			DisplayAndLog("Couldn't find color map.");
		else
		{
			if (!cmap.Load(cmap_path))
				DisplayAndLog("Couldn't load color map.");
		}
	}

	ImageGLCanvas *pCanvas = NULL;
#if USE_OPENGL
	wxFrame *frame = new wxFrame;
	if (opts.bCreateDerivedImages && opts.bUseTextureCompression && opts.eCompressionType == TC_OPENGL)
	{
		frame->Create(g_bld->m_pParentWindow, -1, _T("Texture Compression OpenGL Context"),
			wxPoint(100,400), wxSize(280, 300), wxCAPTION | wxCLIP_CHILDREN);
		pCanvas = new ImageGLCanvas(frame);
	}
#endif

	// make a note of which lods exist
	LODMap lod_existence_map(opts.cols, opts.rows);

	bool bFloat = m_pGrid->IsFloatMode();
	bool bJPEG = (opts.bUseTextureCompression && opts.eCompressionType == TC_JPEG);

	int i, j, lod;
	int total = opts.rows * opts.cols, done = 0;
	for (j = 0; j < opts.rows; j++)
	{
		for (i = 0; i < opts.cols; i++)
		{
			// We might want to skip certain tiles
			if (opts.iMinRow != -1 &&
				(i < opts.iMinCol || i > opts.iMaxCol ||
				 j < opts.iMinRow || j > opts.iMaxRow))
				continue;

			DRECT tile_area;
			tile_area.left = area.left + tile_dim.x * i;
			tile_area.right = area.left + tile_dim.x * (i+1);
			tile_area.bottom = area.bottom + tile_dim.y * j;
			tile_area.top = area.bottom + tile_dim.y * (j+1);

			int col = i;
			int row = opts.rows-1-j;

			// draw our progress in the main view
			if (pView)
				pView->ShowGridMarks(area, opts.cols, opts.rows, col, opts.rows-1-row);

			// Extract the highest LOD we need
			vtElevationGrid base_lod(tile_area, IPoint2(base_tilesize+1, base_tilesize+1),
				bFloat, proj);

			bool bAllInvalid = true;
			bool bAllZero = true;
			int iNumInvalid = 0;
			DPoint2 p;
			int x, y;
			for (y = base_tilesize; y >= 0; y--)
			{
				p.y = area.bottom + (j*tile_dim.y) + ((double)y / base_tilesize * tile_dim.y);
				for (x = 0; x <= base_tilesize; x++)
				{
					p.x = area.left + (i*tile_dim.x) + ((double)x / base_tilesize * tile_dim.x);

					float fvalue = m_pGrid->GetFilteredValue(p);
					base_lod.SetFValue(x, y, fvalue);

					if (fvalue == INVALID_ELEVATION)
						iNumInvalid++;
					else
					{
						bAllInvalid = false;

						// Gather height extents
						if (fvalue < minheight)
							minheight = fvalue;
						if (fvalue > maxheight)
							maxheight = fvalue;
					}
					if (fvalue != 0)
						bAllZero = false;
				}
			}
			// Increment whether we omit or not
			done++;

			// If there is no real data there, omit this tile
			if (bAllInvalid)
				continue;

			// Omit all-zero tiles (flat sea-level) if desired
			if (opts.bOmitFlatTiles && bAllZero)
				continue;

			// Now we know this tile will be included, so note the LODs present
			int base_tile_exponent = vt_log2(base_tilesize);
			lod_existence_map.set(i, j, base_tile_exponent, base_tile_exponent-(opts.numlods-1));

			if (iNumInvalid > 0)
			{
				UpdateProgressDialog2(done*99/total, 0, _("Filling gaps"));

				bool bGood;
				int method = g_Options.GetValueInt(TAG_GAP_FILL_METHOD);
				if (method == 1)
					bGood = base_lod.FillGaps(NULL, progress_callback_minor);
				else if (method == 2)
					bGood = base_lod.FillGapsSmooth(NULL, progress_callback_minor);
				else if (method == 3)
					bGood = (base_lod.FillGapsByRegionGrowing(2, 5, progress_callback_minor) != -1);
				if (!bGood)
					return false;

				opts.iNoDataFilled += iNumInvalid;
			}

			// Create a matching derived texture tileset
			if (opts.bCreateDerivedImages)
			{
				// Create a matching derived texture tileset
				vtDIB dib;
				base_lod.ComputeHeightExtents();

				if (opts.bImageAlpha)
				{
					dib.Create(IPoint2(base_tilesize, base_tilesize), 32);
					base_lod.ColorDibFromElevation(&dib, &cmap, 4000, RGBAi(0,0,0,0));
				}
				else
				{
					dib.Create(IPoint2(base_tilesize, base_tilesize), 24);
					base_lod.ColorDibFromElevation(&dib, &cmap, 4000, RGBi(255,0,0));
				}

				if (opts.draw.m_bShadingQuick)
					base_lod.ShadeQuick(&dib, SHADING_BIAS, true);
				else if (opts.draw.m_bShadingDot)
				{
					FPoint3 light_dir = LightDirection(opts.draw.m_iCastAngle,
						opts.draw.m_iCastDirection);

					// Don't cast shadows for tileset; they won't cast
					//  correctly from one tile to the next.
					base_lod.ShadeDibFromElevation(&dib, light_dir, 1.0f,
						opts.draw.m_fAmbient, opts.draw.m_fGamma, true);
				}

				for (int k = 0; k < opts.numlods; k++)
				{
					vtString fname = MakeFilenameDB(dirname_image, col, row, k);

					int tilesize = base_tilesize >> k;

					vtMiniDatabuf output_buf;
					output_buf.xsize = tilesize;
					output_buf.ysize = tilesize;
					output_buf.zsize = 1;
					output_buf.tsteps = 1;
					output_buf.SetBounds(proj, tile_area);

					int depth = dib.GetDepth() / 8;
					int iUncompressedSize = tilesize * tilesize * depth;
					uchar *rgb_bytes = (uchar *) malloc(iUncompressedSize);

					uchar *dst = rgb_bytes;
					if (opts.bImageAlpha)
					{
						RGBAi rgba;
						for (int ro = 0; ro < base_tilesize; ro += (1<<k))
							for (int co = 0; co < base_tilesize; co += (1<<k))
							{
								dib.GetPixel32(co, ro, rgba);
								*dst++ = rgba.r;
								*dst++ = rgba.g;
								*dst++ = rgba.b;
								*dst++ = rgba.a;
							}
					}
					else
					{
						RGBi rgb;
						for (int ro = 0; ro < base_tilesize; ro += (1<<k))
							for (int co = 0; co < base_tilesize; co += (1<<k))
							{
								dib.GetPixel24(co, ro, rgb);
								*dst++ = rgb.r;
								*dst++ = rgb.g;
								*dst++ = rgb.b;
							}
					}

					// Write and optionally compress the image
					WriteMiniImage(fname, opts, rgb_bytes, output_buf,
						iUncompressedSize, pCanvas);

					// Free the uncompressed image
					free(rgb_bytes);
				}
			}

			for (lod = 0; lod < opts.numlods; lod++)
			{
				int tilesize = base_tilesize >> lod;

				vtString fname = MakeFilenameDB(dirname, col, row, lod);

				// make a message for the progress dialog
				wxString msg;
				msg.Printf(_("Writing tile '%hs', size %dx%d"),
					(const char *)fname, tilesize, tilesize);
				UpdateProgressDialog2(done*99/total, 0, msg);

				vtMiniDatabuf buf;
				buf.SetBounds(proj, tile_area);
				buf.alloc(tilesize+1, tilesize+1, 1, 1, bFloat ? 2 : 1);
				float *fdata = (float *) buf.data;
				short *sdata = (short *) buf.data;

				DPoint2 p;
				for (int y = base_tilesize; y >= 0; y -= (1<<lod))
				{
					p.y = area.bottom + (j*tile_dim.y) + ((double)y / base_tilesize * tile_dim.y);
					for (int x = 0; x <= base_tilesize; x += (1<<lod))
					{
						p.x = area.left + (i*tile_dim.x) + ((double)x / base_tilesize * tile_dim.x);

						if (bFloat)
						{
							*fdata = base_lod.GetFilteredValue(p);
							fdata++;
						}
						else
						{
							*sdata = (short) base_lod.GetFilteredValue(p);
							sdata++;
						}
					}
				}
				if (buf.savedata(fname) == 0)
				{
					// what should we do if writing a tile fails?
				}
			}
		}
	}
Ejemplo n.º 14
0
void Group::MenuGroup(int id) {
    Group g;
    ZERO(&g);
    g.visible = true;
    g.color = RGBi(100, 100, 100);
    g.scale = 1;

    if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
        strcpy(g.impFile, RecentFile[id-RECENT_IMPORT]);
        id = GraphicsWindow::MNU_GROUP_IMPORT;
    }

    SS.GW.GroupSelection();

    switch(id) {
        case GraphicsWindow::MNU_GROUP_3D:
            g.type = DRAWING_3D;
            g.name.strcpy("sketch-in-3d");
            break;

        case GraphicsWindow::MNU_GROUP_WRKPL:
            g.type = DRAWING_WORKPLANE;
            g.name.strcpy("sketch-in-plane");
            if(gs.points == 1 && gs.n == 1) {
                g.subtype = WORKPLANE_BY_POINT_ORTHO;

                Vector u = SS.GW.projRight, v = SS.GW.projUp;
                u = u.ClosestOrtho();
                v = v.Minus(u.ScaledBy(v.Dot(u)));
                v = v.ClosestOrtho();

                g.predef.q = Quaternion::From(u, v);
                g.predef.origin = gs.point[0];
            } else if(gs.points == 1 && gs.lineSegments == 2 && gs.n == 3) {
                g.subtype = WORKPLANE_BY_LINE_SEGMENTS;

                g.predef.origin = gs.point[0];
                g.predef.entityB = gs.entity[0];
                g.predef.entityC = gs.entity[1];

                Vector ut = SK.GetEntity(g.predef.entityB)->VectorGetNum();
                Vector vt = SK.GetEntity(g.predef.entityC)->VectorGetNum();
                ut = ut.WithMagnitude(1);
                vt = vt.WithMagnitude(1);

                if(fabs(SS.GW.projUp.Dot(vt)) < fabs(SS.GW.projUp.Dot(ut))) {
                    SWAP(Vector, ut, vt);
                    g.predef.swapUV = true;
                }
                if(SS.GW.projRight.Dot(ut) < 0) g.predef.negateU = true;
                if(SS.GW.projUp.   Dot(vt) < 0) g.predef.negateV = true;
            } else {
                Error("Bad selection for new sketch in workplane. This "
                      "group can be created with:\n\n"
                      "    * a point (orthogonal to coordinate axes, "
                             "through the point)\n"
                      "    * a point and two line segments (parallel to the "
                             "lines, through the point)\n");
                return;
            }
            break;

        case GraphicsWindow::MNU_GROUP_EXTRUDE:
            if(!SS.GW.LockedInWorkplane()) {
                Error("Select a workplane (Sketch -> In Workplane) before "
                      "extruding. The sketch will be extruded normal to the "
                      "workplane.");
                return;
            }
            g.type = EXTRUDE;
            g.opA = SS.GW.activeGroup;
            g.predef.entityB = SS.GW.ActiveWorkplane();
            g.subtype = ONE_SIDED;
            g.name.strcpy("extrude");
            break;

        case GraphicsWindow::MNU_GROUP_LATHE:
            if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
                g.predef.origin = gs.point[0];
                g.predef.entityB = gs.vector[0];
            } else if(gs.lineSegments == 1 && gs.n == 1) {
                g.predef.origin = SK.GetEntity(gs.entity[0])->point[0];
                g.predef.entityB = gs.entity[0];
                // since a line segment is a vector
            } else {
                Error("Bad selection for new lathe group. This group can "
                      "be created with:\n\n"
                      "    * a point and a line segment or normal "
                               "(revolved about an axis parallel to line / "
                               "normal, through point)\n"
                      "    * a line segment (revolved about line segment)\n");
                return;
            }
            g.type = LATHE;
            g.opA = SS.GW.activeGroup;
            g.name.strcpy("lathe");
            break;

        case GraphicsWindow::MNU_GROUP_ROT: {
            if(gs.points == 1 && gs.n == 1 && SS.GW.LockedInWorkplane()) {
                g.predef.origin = gs.point[0];
                Entity *w = SK.GetEntity(SS.GW.ActiveWorkplane());
                g.predef.entityB = w->Normal()->h;
                g.activeWorkplane = w->h;
            } else if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
                g.predef.origin = gs.point[0];
                g.predef.entityB = gs.vector[0];
            } else {
                Error("Bad selection for new rotation. This group can "
                      "be created with:\n\n"
                      "    * a point, while locked in workplane (rotate "
                            "in plane, about that point)\n"
                      "    * a point and a line or a normal (rotate about "
                            "an axis through the point, and parallel to "
                            "line / normal)\n");
                return;
            }
            g.type = ROTATE;
            g.opA = SS.GW.activeGroup;
            g.valA = 3;
            g.subtype = ONE_SIDED;
            g.name.strcpy("rotate");
            break;
        }

        case GraphicsWindow::MNU_GROUP_TRANS:
            g.type = TRANSLATE;
            g.opA = SS.GW.activeGroup;
            g.valA = 3;
            g.subtype = ONE_SIDED;
            g.predef.entityB = SS.GW.ActiveWorkplane();
            g.activeWorkplane = SS.GW.ActiveWorkplane();
            g.name.strcpy("translate");
            break;

        case GraphicsWindow::MNU_GROUP_IMPORT: {
            g.type = IMPORTED;
            g.opA = SS.GW.activeGroup;
            if(strlen(g.impFile) == 0) {
                if(!GetOpenFile(g.impFile, SLVS_EXT, SLVS_PATTERN)) return;
            }

            // Assign the default name of the group based on the name of
            // the imported file.
            char groupName[MAX_PATH];
            strcpy(groupName, g.impFile);
            char *dot = strrchr(groupName, '.');
            if(dot) *dot = '\0';

            char *s, *start = groupName;
            for(s = groupName; *s; s++) {
                if(*s == '/' || *s == '\\') {
                    start = s + 1;
                } else if(isalnum(*s)) {
                    // do nothing, valid character
                } else {
                    // convert invalid characters (like spaces) to dashes
                    *s = '-';
                }
            }
            if(strlen(start) > 0) {
                g.name.strcpy(start);
            } else {
                g.name.strcpy("import");
            }

            g.meshCombine = COMBINE_AS_ASSEMBLE;
            break;
        }

        default: oops();
    }
    SS.GW.ClearSelection();
    SS.UndoRemember();

    SK.group.AddAndAssignId(&g);
    Group *gg = SK.GetGroup(g.h);

    if(gg->type == IMPORTED) {
        SS.ReloadAllImported();
    }
    gg->clean = false;
    SS.GW.activeGroup = gg->h;
    SS.GenerateAll();
    if(gg->type == DRAWING_WORKPLANE) {
        // Can't set the active workplane for this one until after we've
        // regenerated, because the workplane doesn't exist until then.
        gg->activeWorkplane = gg->h.entity(0);
    }
    gg->Activate();
    SS.GW.AnimateOntoWorkplane();
    TextWindow::ScreenSelectGroup(0, gg->h.v);
    SS.later.showTW = true;
}
Ejemplo n.º 15
0
void StructVisitorGML::startElement(const char *name, const XMLAttributes &atts)
{
	const char *attval;
	float f;

	// clear data at the start of each element
	m_data = "";

	if (m_state == 0 && !strcmp(name, "StructureCollection"))
	{
		m_state = 1;
		return;
	}

	if (m_state == 1)
	{
		if (!strcmp(name, "Building"))
		{
			m_pBuilding = m_pSA->NewBuilding();
			m_pStructure = m_pBuilding;
			m_state = 2;
			m_iLevel = 0;
		}
		else if (!strcmp(name, "Linear"))
		{
			m_pFence = m_pSA->NewFence();
			m_pFence->GetParams().Blank();
			m_pStructure = m_pFence;

			// support obsolete attribute
			attval = atts.getValue("Height");
			if (attval)
			{
				f = (float) atof(attval);
				m_pFence->GetParams().m_fPostHeight = f;
				m_pFence->GetParams().m_fConnectTop = f;
			}
			m_state = 10;
		}
		else if (!strcmp(name, "Imported"))
		{
			m_pInstance = m_pSA->NewInstance();
			m_pStructure = m_pInstance;

			m_state = 20;
		}
		attval = atts.getValue("ElevationOffset");
		if (attval)
			m_pStructure->SetElevationOffset((float) atof(attval));
		attval = atts.getValue("Absolute");
		if (attval)
			m_pStructure->SetAbsolute(*attval == 't');

		return;
	}

	if (m_state == 2)	// Building
	{
		if (!strcmp(name, "Level"))
		{
			m_pLevel = m_pBuilding->CreateLevel();
			attval = atts.getValue("FloorHeight");
			if (attval)
				m_pLevel->m_fStoryHeight = (float) atof(attval);
			attval = atts.getValue("StoryCount");
			if (attval)
				m_pLevel->m_iStories = atoi(attval);
			m_state = 3;
			m_iEdge = 0;
		}
		return;
	}

	if (m_state == 3)	// Level
	{
		if (!strcmp(name, "Footprint"))
		{
			m_state = 4;
			m_Footprint.clear();
		}
		else if (!strcmp(name, "Edge"))
		{
			m_pEdge = m_pLevel->GetEdge(m_iEdge);
			if (m_pEdge)	// safety check
			{
				m_pEdge->m_Features.clear();

				attval = atts.getValue("Material");
				if (attval)
				{
					vtMaterialDescriptorArray *mats = GetGlobalMaterials();
					m_pEdge->m_pMaterial = mats->FindName(attval);
					if (m_pEdge->m_pMaterial == NULL)
					{
						// What to do when a VTST references a material that
						// we don't have?  We don't want to lose the material
						// name information, and we also don't want to crash
						// later with a NULL material.  So, make a dummy.
						vtMaterialDescriptor *mat;
						mat = new vtMaterialDescriptor(attval, "", VT_MATERIAL_COLOUR);
						mat->SetRGB(RGBi(255,255,255));	// white means: missing
						mats->Append(mat);
						m_pEdge->m_pMaterial = &mat->GetName();
					}
				}
				attval = atts.getValue("Color");
				if (attval)
					m_pEdge->m_Color = ParseHexColor(attval);
				attval = atts.getValue("Slope");
				if (attval)
					m_pEdge->m_iSlope = atoi(attval);
				attval = atts.getValue("EaveLength");
				if (attval)
					m_pEdge->m_fEaveLength = (float) atof(attval);
				attval = atts.getValue("Facade");
				if (attval)
					m_pEdge->m_Facade = attval;
			}

			m_state = 7;	// in Edge
		}
		return;
	}

	if (m_state == 4)	// Footprint
	{
		if (!strcmp(name, "gml:outerBoundaryIs"))
			m_state = 5;
		if (!strcmp(name, "gml:innerBoundaryIs"))
			m_state = 6;
	}

	if (m_state == 5)	// Footprint outerBoundaryIs
	{
		// nothing necessary here, catch the end of element
	}

	if (m_state == 6)	// Footprint innerBoundaryIs
	{
		// nothing necessary here, catch the end of element
	}

	if (m_state == 7)	// Edge
	{
		if (!strcmp(name, "EdgeElement"))
		{
			vtEdgeFeature ef;

			attval = atts.getValue("Type");
			if (attval)
				ef.m_code = vtBuilding::GetEdgeFeatureValue(attval);
			attval = atts.getValue("Begin");
			if (attval)
				ef.m_vf1 = (float) atof(attval);
			attval = atts.getValue("End");
			if (attval)
				ef.m_vf2 = (float) atof(attval);
			if (m_pEdge)
				m_pEdge->m_Features.push_back(ef);
		}
	}

	if (m_state == 10)	// Linear
	{
		vtLinearParams &param = m_pFence->GetParams();
		if (!strcmp(name, "Path"))
		{
			m_state = 11;
		}
		else if (!strcmp(name, "Posts"))
		{
			// this linear structure has posts
			const char *type = atts.getValue("Type");
			if (type)
				param.m_PostType = type;
			else
				param.m_PostType = "none";

			const char *spacing = atts.getValue("Spacing");
			if (spacing)
				param.m_fPostSpacing = (float)atof(spacing);

			const char *height = atts.getValue("Height");
			if (height)
				param.m_fPostHeight = (float)atof(height);

			const char *size = atts.getValue("Size");
			if (size)
			{
				FPoint3 postsize;
				sscanf(size, "%f, %f", &postsize.x, &postsize.z);
				param.m_fPostWidth = postsize.x;
				param.m_fPostDepth = postsize.z;
			}

			const char *exten = atts.getValue("Extension");
			if (exten)
				param.m_PostExtension = exten;
		}
		else if (!strcmp(name, "Connect"))
		{
			const char *type = atts.getValue("Type");
			if (type)
			{
				// Older format version had string "none", "wire", or a material for type
				// Newer format has integer 0,1,2,3 for none,wire,simple,profile
				if (*type >= '0' && *type <= '9')
					param.m_iConnectType = atoi(type);
				else
				{
					// Convert old to new
					if (!strcmp(type, "none"))
						param.m_iConnectType = 0;
					else if (!strcmp(type, "wire"))
						param.m_iConnectType = 1;
					else
					{
						param.m_iConnectType = 2;
						param.m_ConnectMaterial = type;
					}
				}
			}
			else
				param.m_iConnectType = 0;

			attval = atts.getValue("Material");
			if (attval)
				param.m_ConnectMaterial = attval;

			attval = atts.getValue("Top");
			if (attval)
				param.m_fConnectTop = (float)atof(attval);
			attval = atts.getValue("Bottom");
			if (attval)
				param.m_fConnectBottom = (float)atof(attval);
			attval = atts.getValue("Width");
			if (attval)
				param.m_fConnectWidth = (float)atof(attval);
			attval = atts.getValue("Slope");
			if (attval)
				param.m_iConnectSlope = atoi(attval);
			attval = atts.getValue("ConstantTop");
			if (attval)
				param.m_bConstantTop = (*attval == 't');

			attval = atts.getValue("Profile");
			if (attval)
				param.m_ConnectProfile = attval;
		}
	}
	if (m_state == 20)	// Imported
	{
		if (!strcmp(name, "Location"))
		{
			m_state = 21;
		}
	}
}
Ejemplo n.º 16
0
void Entity::DrawOrGetDistance(void) {
    if(!IsVisible()) return;

    Group *g = SK.GetGroup(group);

    switch(type) {
        case POINT_N_COPY:
        case POINT_N_TRANS:
        case POINT_N_ROT_TRANS:
        case POINT_N_ROT_AA:
        case POINT_IN_3D:
        case POINT_IN_2D: {
            Vector v = PointGetNum();
            dogd.refp = v;

            if(dogd.drawing) {
                double s = 3.5;
                Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale);
                Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale);

                ssglColorRGB(Style::Color(Style::DATUM));
                ssglDepthRangeOffset(6);
                glBegin(GL_QUADS);
                    ssglVertex3v(v.Plus (r).Plus (d));
                    ssglVertex3v(v.Plus (r).Minus(d));
                    ssglVertex3v(v.Minus(r).Minus(d));
                    ssglVertex3v(v.Minus(r).Plus (d));
                glEnd();
                ssglDepthRangeOffset(0);
            } else {
                Point2d pp = SS.GW.ProjectPoint(v);
                dogd.dmin = pp.DistanceTo(dogd.mp) - 6;
            }
            break;
        }

        case NORMAL_N_COPY:
        case NORMAL_N_ROT:
        case NORMAL_N_ROT_AA:
        case NORMAL_IN_3D:
        case NORMAL_IN_2D: {
            int i;
            for(i = 0; i < 2; i++) {
                if(i == 0 && !SS.GW.showNormals) {
                    // When the normals are hidden, we will continue to show
                    // the coordinate axes at the bottom left corner, but
                    // not at the origin.
                    continue;
                }

                hRequest hr = h.request();
                // Always draw the x, y, and z axes in red, green, and blue;
                // brighter for the ones at the bottom left of the screen,
                // dimmer for the ones at the model origin.
                int f = (i == 0 ? 100 : 255);
                if(hr.v == Request::HREQUEST_REFERENCE_XY.v) {
                    ssglColorRGB(RGBi(0, 0, f));
                } else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) {
                    ssglColorRGB(RGBi(f, 0, 0));
                } else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) {
                    ssglColorRGB(RGBi(0, f, 0));
                } else {
                    ssglColorRGB(Style::Color(Style::NORMALS));
                    if(i > 0) break;
                }

                Quaternion q = NormalGetNum();
                Vector tail;
                if(i == 0) {
                    tail = SK.GetEntity(point[0])->PointGetNum();
                    glLineWidth(1);
                } else {
                    // Draw an extra copy of the x, y, and z axes, that's
                    // always in the corner of the view and at the front.
                    // So those are always available, perhaps useful.
                    double s = SS.GW.scale;
                    double h = 60 - SS.GW.height/2;
                    double w = 60 - SS.GW.width/2;
                    tail = SS.GW.projRight.ScaledBy(w/s).Plus(
                           SS.GW.projUp.   ScaledBy(h/s)).Minus(SS.GW.offset);
                    ssglDepthRangeLockToFront(true);
                    glLineWidth(2);
                }

                Vector v = (q.RotationN()).WithMagnitude(50/SS.GW.scale);
                Vector tip = tail.Plus(v);
                LineDrawOrGetDistance(tail, tip);

                v = v.WithMagnitude(12/SS.GW.scale);
                Vector axis = q.RotationV();
                LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis, 0.6)));
                LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis,-0.6)));
            }
            ssglDepthRangeLockToFront(false);
            break;
        }

        case DISTANCE:
        case DISTANCE_N_COPY:
            // These are used only as data structures, nothing to display.
            break;

        case WORKPLANE: {
            Vector p;
            p = SK.GetEntity(point[0])->PointGetNum();

            Vector u = Normal()->NormalU();
            Vector v = Normal()->NormalV();

            double s = (min(SS.GW.width, SS.GW.height))*0.45/SS.GW.scale;

            Vector us = u.ScaledBy(s);
            Vector vs = v.ScaledBy(s);

            Vector pp = p.Plus (us).Plus (vs);
            Vector pm = p.Plus (us).Minus(vs);
            Vector mm = p.Minus(us).Minus(vs), mm2 = mm;
            Vector mp = p.Minus(us).Plus (vs);

            glLineWidth(1);
            ssglColorRGB(Style::Color(Style::NORMALS));
            glEnable(GL_LINE_STIPPLE);
            glLineStipple(3, 0x1111);
            if(!h.isFromRequest()) {
                mm = mm.Plus(v.ScaledBy(60/SS.GW.scale));
                mm2 = mm2.Plus(u.ScaledBy(60/SS.GW.scale));
                LineDrawOrGetDistance(mm2, mm);
            }
            LineDrawOrGetDistance(pp, pm);
            LineDrawOrGetDistance(pm, mm2);
            LineDrawOrGetDistance(mm, mp);
            LineDrawOrGetDistance(mp, pp);
            glDisable(GL_LINE_STIPPLE);

            char *str = DescriptionString()+5;
            double th = DEFAULT_TEXT_HEIGHT;
            if(dogd.drawing) {
                ssglWriteText(str, th, mm2, u, v, NULL, NULL);
            } else {
                Vector pos = mm2.Plus(u.ScaledBy(ssglStrWidth(str, th)/2)).Plus(
                                      v.ScaledBy(ssglStrHeight(th)/2));
                Point2d pp = SS.GW.ProjectPoint(pos);
                dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 10);
                // If a line lies in a plane, then select the line, not
                // the plane.
                dogd.dmin += 3;
            }
            break;
        }

        case LINE_SEGMENT:
        case CIRCLE:
        case ARC_OF_CIRCLE:
        case CUBIC:
        case CUBIC_PERIODIC:
        case TTF_TEXT:
            // Nothing but the curve(s).
            break;

        case FACE_NORMAL_PT:
        case FACE_XPROD:
        case FACE_N_ROT_TRANS:
        case FACE_N_TRANS:
        case FACE_N_ROT_AA:
            // Do nothing; these are drawn with the triangle mesh
            break;

        default:
            oops();
    }

    // And draw the curves; generate the rational polynomial curves for
    // everything, then piecewise linearize them, and display those.
    SEdgeList sel;
    ZERO(&sel);
    GenerateEdges(&sel, true);
    int i;
    for(i = 0; i < sel.l.n; i++) {
        SEdge *se = &(sel.l.elem[i]);
        LineDrawOrGetDistance(se->a, se->b, true);
    }
    sel.Clear();
}